package com.basic.library.httpmanager.task;

import static com.basic.library.base.ConstKt.useDefaultLoadingView;

import com.basic.library.Interface.MCallback;
import com.basic.library.base.BaseApplication;
import com.basic.library.bean.DataResult;
import com.basic.library.utils.JsonUtils;
import com.basic.library.utils.toast.ToastCompat;
import com.google.gson.internal.$Gson$Types;
import com.zhouyou.http.cache.model.CacheMode;
import com.zhouyou.http.exception.ApiException;
import com.zhouyou.http.request.BaseRequest;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;

import io.reactivex.disposables.Disposable;


/**
 * @author jiangyun
 * @description 网络请求管理
 * 若需改写DataResult， 需完全重写此类
 */
public class RHttpManager {

    private MCallback BaseRequestCallback;
    private CacheMode[] cacheMode;
    private String tag;
    private LView mLView;

    public static RHttpManager obj() {
        return new RHttpManager();
    }

    /**
     * @return 当页面中即有SwipeRefreshLayout下拉刷新的loading动作，也有其它接口的loading动作（如删除等待框）
     * 即有2种不同的回调操作，其它接口用 obj1(),下拉刷新用obj()
     * 当只存在一种loading动作 ， 可以用 obj() 或 obj1()
     * <p>
     * 如不区分，必须在Activity / Fragment 中重写loading和dismiss的相关操作
     */
    public static RHttpManager obj1() {
        return new RHttpManager().defaultLoading();
    }

    /**
     * @param tag 区分接口
     * @return
     */
    public RHttpManager tag(String tag) {
        this.tag = tag;
        return this;
    }

    /**
     * 有SwipeRefreshLayout布局时，默认用SwipeRefreshLayout的刷新动画，没有时用‘等待动画’
     * useDefaultLoadingView的作用 ** 强制不用SwipeRefreshLayout
     */
    public RHttpManager defaultLoading() {
        return tag(useDefaultLoadingView);
    }

    public RHttpManager addLView(LView lView) {
        this.mLView = lView;
        return this;
    }

    public enum Mode {
        get, post, postBody, upload, put, putBody, delete
    }

    /**
     * 在回调中单独添加自己想要的Request配置
     *
     * @param callback
     * @return
     */
    public <T extends BaseRequest> RHttpManager request(MCallback<T> callback) {
        BaseRequestCallback = callback;
        return this;
    }

    /**
     * 调用该函数，cacheMode==null时默认CacheMode.CACHEANDREMOTEDISTINCT，其它类型需单独传入
     * 不调用该函数时。默认 不缓存
     *
     * @param cacheMode
     * @return
     */
    public RHttpManager cache(CacheMode... cacheMode) {
        if (cacheMode != null && cacheMode.length != 0)
            this.cacheMode = cacheMode;
        else
            this.cacheMode = new CacheMode[]{CacheMode.CACHEANDREMOTEDISTINCT};
        return this;
    }

    /**
     * 表单提交
     *
     * @param iView          传入实现IView的activity。可为null
     * @param url            可以是全路径 也可以是相对路径
     * @param map
     * @param resultCallback
     * @return
     */
    public void postAsync(final IView iView, String url
            , Map<String, String> map, final ResultCallback resultCallback) {
        ApiHttpService apiHttpService = connent(iView, url, map, resultCallback, Mode.post);
        if (BaseRequestCallback != null) {
            BaseRequestCallback.onResponse(apiHttpService.getEasyHttp());
        }
        apiHttpService.execute();
    }

    /**
     * body提交
     */
    public void postbodyAsync(final IView iView, String url
            , Object map, final ResultCallback resultCallback) {
        ApiHttpService apiHttpService = connent(iView, url, map, resultCallback, Mode.postBody);
        if (BaseRequestCallback != null) {
            BaseRequestCallback.onResponse(apiHttpService.getEasyHttp());
        }
        apiHttpService.execute();
    }

    public void putAsync(final IView iView, String url
            , Object map, final ResultCallback resultCallback) {
        ApiHttpService apiHttpService = connent(iView, url, map, resultCallback, Mode.put);
        if (BaseRequestCallback != null) {
            BaseRequestCallback.onResponse(apiHttpService.getEasyHttp());
        }
        apiHttpService.execute();
    }

    /**
     * body提交
     */
    public void putBodyAsync(final IView iView, String url
            , Object map, final ResultCallback resultCallback) {
        ApiHttpService apiHttpService = connent(iView, url, map, resultCallback, Mode.putBody);
        if (BaseRequestCallback != null) {
            BaseRequestCallback.onResponse(apiHttpService.getEasyHttp());
        }
        apiHttpService.execute();
    }

    public void deleteAsync(final IView iView, String url
            , Object map, final ResultCallback resultCallback) {
        ApiHttpService apiHttpService = connent(iView, url, map, resultCallback, Mode.delete);
        if (BaseRequestCallback != null) {
            BaseRequestCallback.onResponse(apiHttpService.getEasyHttp());
        }
        apiHttpService.execute();
    }

    public void getAsync(final IView iView, String url
            , Map<String, String> map, final ResultCallback resultCallback) {
        ApiHttpService apiHttpService = connent(iView, url, map, resultCallback, Mode.get);
        if (BaseRequestCallback != null) {
            BaseRequestCallback.onResponse(apiHttpService.getEasyHttp());
        }
        apiHttpService.execute();
    }

    public IView uploadAsync(final IView iView, String url
            , Map<String, Object> map, final ResultCallback resultCallback) {
        connent(iView, url, map, resultCallback, Mode.upload);
        return iView;
    }

    Mode mode;

    public RHttpManager post() {
        mode = Mode.post;
        return this;
    }

    public RHttpManager postBody() {
        mode = Mode.postBody;
        return this;
    }

    public RHttpManager put() {
        mode = Mode.put;
        return this;
    }

    public RHttpManager putBody() {
        mode = Mode.putBody;
        return this;
    }

    public RHttpManager delete() {
        mode = Mode.delete;
        return this;
    }

    public RHttpManager get() {
        mode = Mode.get;
        return this;
    }

    public IView async(final IView iView, String url
            , Object map, final ResultCallback resultCallback) {
        if (mode == null)
            throw new NullPointerException("先初始化接口方式~~");
        ApiHttpService apiHttpService = connent(iView, url, map, resultCallback, mode);
        if (BaseRequestCallback != null) {
            BaseRequestCallback.onResponse(apiHttpService.getEasyHttp());
        }
        apiHttpService.execute();
        return iView;
    }

    /**
     * 去找真正的http交互ApiHttpService
     *
     * @param iView
     * @param url
     * @param map
     * @param resultCallback
     * @param mode
     * @return
     */
    private ApiHttpService connent(final IView iView, String url
            , Object map, final ResultCallback resultCallback, Mode mode) {
        if (iView != null)
            iView.loading(tag);
        if (mLView != null)
            mLView.setEmptyCover(LViewEnum.Start);
        return new ApiHttpService().setCallBack(new ApiHttpService.ApiHttpCallBack() {
            @Override
            public void onGetData(String dataResult) {
                resultCallback.isFromCache = isFromCache;
                Object result = null;
                try {
                    result = JsonUtils.fromJson(dataResult, resultCallback.mType);
//                    result = JsonUtils.fromJsonNullToEmptyString(dataResult, resultCallback.mType);
                } catch (Exception e) { //类型转换失败
                    result = dataResult;
                    e.printStackTrace();
                } finally {
                    try {
                        doData(iView, result, resultCallback);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            void onSubscribe(Disposable d) {
                if (iView != null) {
                    iView.onSubscribe(d);
                }
            }

            @Override
            void onError(Throwable e) {
                if (iView != null) {
                    iView.dismiss(null); //区分 成功返回的tag
                }
                if (mLView != null)
                    mLView.setEmptyCover(LViewEnum.Error);
                resultCallback.onError(e);
            }
        }).cache(cacheMode).sendData(url, map, mode);
    }

    protected void doData(IView iView, Object result0, ResultCallback resultCallback) throws Exception {
//        //数据预处理
//        if (CommonUtils.disposeSoapDataException(result0)) {
//            return;
//        }
        DataResult result;
        if (result0 instanceof DataResult) {
            result = (DataResult) result0;
            if (mLView != null) {
                if (result.getData() == null)
                    mLView.setEmptyCover(LViewEnum.DataUseless);
                else
                    mLView.setEmptyCover(LViewEnum.DataUseful);
            }

            if (result.getSuccess() != null && result.getSuccess()) {
                resultCallback.onResponse(result);
            } else {
                if (!resultCallback.onInterceptToast())
                    ToastCompat.show(result.getMessage());
            }
        } else {
            resultCallback.onResponse(result0); //不是DataResult基类，直接返回
        }
        if (iView != null)
            iView.dismiss(tag);
    }

    public abstract static class ResultCallback<T> {
        Type mType;
        public boolean isFromCache = false;

        public ResultCallback() {
            mType = getSuperclassTypeParameter(getClass());
        }

        static Type getSuperclassTypeParameter(Class<?> subclass) {
            Type superclass = subclass.getGenericSuperclass();
            if (superclass instanceof Class) {
                throw new RuntimeException("Missing type parameter.");
            }
            ParameterizedType parameterized = (ParameterizedType) superclass;
            return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
        }

        //继承，会有弹框提示
        public void onError(Throwable throwable) {
            try {
                judgeError((ApiException) throwable);
            } catch (ClassCastException e) {
                ToastCompat.show("未知错误");
            }
        }

        /**
         * 判断错误类型
         *
         * @param error
         */
        private void judgeError(ApiException error) {
            if (error.getCode() == 401) {
                BaseApplication.getInstance().reLogin();
                ToastCompat.show("登录过期");
            } else if (error.getCode() == 500) {
                ToastCompat.show("网络繁忙，请稍候再试");
            } else if (error.getCode() == 404) {
                ToastCompat.show("服务器繁忙，请稍候再试");
            } else if (error.getCode() == 1009) {
                ToastCompat.show("网络信号无法达到");
            } else {
                ToastCompat.show(error.getMessage());
            }

        }

        public abstract void onResponse(T response) throws Exception;

        /**
         * 是否拦截toast
         */
        public boolean onInterceptToast() {
            return false;
        }
    }
}
